#ifndef _chipmnunk_object_h_
#define _chipmnunk_object_h_
#include "graphic_public.h"
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <boost/scoped_array.hpp>
#include <boost/foreach.hpp>
#include <boost/type_traits/is_same.hpp>
#include <list>
#include <string>
#include <math.h>
#include <map>
#include <iostream>
#include <boost/tuple/tuple.hpp>
#ifdef WIN32
#include "../chipmuck/chipmunk.h"
#else
#include "chipmunk.h"
#endif


#include "graphic_polygon.h"

#ifndef WIN32
#include <OpenGLES/ES1/gl.h>
#include "ExTexture2D.h"
#else
#include "CTexture.h"
#endif

typedef cpConstraint cpJoint;
#ifndef WIN32
typedef ExTexture2D texture_t;
#else
typedef CTexture texture_t;
#endif

const int kANIMATION_INTERVAL = 30;
const int kHAPPY_ANIMATION_INTERVAL = 15;
const float_t kSegmentShapeThickness = 1.0f;

typedef struct tagObjDelegate{
	boost::function<void(cpSpace *space)> _remove_from_space;
	boost::function<void()> _free;
	boost::function<bool(cpSpace *space, const point2d_t a, const point2d_t b, tagObjDelegate& d0, tagObjDelegate &d1)> _intersect;
} obj_delegate_t;


//µ⁄“ª∏ˆ∑µªÿ÷µ±Ì æ «∑Òø…“‘±ª’®ø™£¨µ⁄∂˛∏ˆ±Ì æ±æ…ÌŒÔÃÂø…“‘±¨’®
typedef boost::tuple<bool, bool> t_is_explosive;
typedef struct
{
	boost::function<void()> render_action;
	boost::function<t_is_explosive()> is_explosive;
	boost::function<const void*()> _getObjInfo;
	boost::function<void(cpSpace *space)> _remove_from_space;
	boost::function<void()> _free;
}action_t;
namespace JointsManager {
	void updateJoints(cpBody *original, 
		cpBody *clip_part0, const point2d_t *vts0, int num0, point2d_t gravity_center0, 
		cpBody *clip_part1, /*const point2d_t *vts1, int num1,*/ point2d_t gravity_center1);
};

class ObjectManager
{
public:
	template <typename T> static T *create_without_register() {return new T();}
	template <typename T>
	T *create() {
		T *ret = new T();
		obj_delegate_t tmp;
		GetObjDelegate(ret, tmp);
		m_resource.push_back(tmp);
		return ret;
	}
	
	template <typename T>
	inline static void GetObjDelegate(T *obj, obj_delegate_t &tmp) {
		tmp._remove_from_space = boost::bind(&T::Remove, obj, _1);
		tmp._free = boost::bind(&T::free_self, obj);
		tmp._intersect = boost::bind(&T::intersect, obj, _1, _2, _3, _4, _5);
	}
	bool Intersect(const point2d_t a, const point2d_t b);
	
public:
	ObjectManager(cpSpace *space) : m_space(space) {}
	~ObjectManager() {
		BOOST_FOREACH(obj_delegate_t &tmp, m_resource) {
			tmp._free();
		}
		m_resource.clear();
	}
private:
	cpSpace *m_space;
	std::list<obj_delegate_t> m_resource;
};


template<typename T> class NullPolicy {};
template <typename T> struct DefaultIntersectPolicy {
	inline bool intersect(cpSpace *space, const point2d_t a, const point2d_t b, 
		obj_delegate_t& d0, obj_delegate_t &d1) {return false;}
};
template <typename T> struct DefaultRenderPolicy{ inline void render() {} };

template <
template <typename> class InitializePolicy, 
template <typename> class ActionPolicy = DefaultRenderPolicy,
template <typename> class OtherPolicy0 = DefaultIntersectPolicy,
template <typename> class OtherPolicy1 = NullPolicy
>
class CPhysicsObject : public InitializePolicy<CPhysicsObject<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> >, 
	public ActionPolicy<CPhysicsObject<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> >,
	public OtherPolicy0<CPhysicsObject<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> >,
	public OtherPolicy1<CPhysicsObject<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> >
{
public:
	typedef InitializePolicy<CPhysicsObject<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> > initialize_policy_t;
	typedef ActionPolicy<CPhysicsObject<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> > action_policy_t;
	typedef OtherPolicy0<CPhysicsObject<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> > other_policy0_t;
	typedef CPhysicsObject<InitializePolicy, ActionPolicy, OtherPolicy0, OtherPolicy1> this_type;

	inline cpBody *GetBoby() {return m_pBoby;}
	inline cpShape *GetShape() {return m_pShape;}
	inline void SetActions() {
		m_actions.render_action = boost::bind(&CPhysicsObject::render, this);
		m_actions._remove_from_space = boost::bind(&CPhysicsObject::Remove, this, _1);
		m_actions._free = boost::bind(&CPhysicsObject::free_self, this);
		m_pShape->data = &m_actions;
	}

	void Remove(cpSpace *space) {
		return initialize_policy_t::Remove(space);
	}
	//Chipmunk uses the Coulomb friction model, a value of 0.0 is frictionless
	inline void SetFrictionCoefficient(cpFloat u){ ASSERT(m_pShape); m_pShape->u = u;}

	//Elasticity of the shape. A value of 0.0 gives no bounce, while a value of 1.0 will give a  perfect bounce.
	inline void SetElasticity(cpFloat e){ ASSERT(m_pShape); m_pShape->e = e;}
	inline void SetFrictionAndElasticity(cpFloat u = 0.5, cpFloat e = 1.0) {
		SetFrictionCoefficient(u); SetElasticity(e);
	}

	static void free_self(CPhysicsObject *pThis){
		delete pThis;
	}

	bool intersect(cpSpace *space, const point2d_t a, const point2d_t b, obj_delegate_t& d0, obj_delegate_t &d1) {
		return other_policy0_t::intersect(space, a, b, d0, d1);
	}

	void render() {
		return action_policy_t::render();
	}

public:
	cpBody *m_pBoby;
	cpShape *m_pShape;
	action_t m_actions;
private:
	CPhysicsObject() : m_pBoby(NULL), m_pShape(NULL) {}
	friend class ObjectManager;
};

template <typename T>
class StaticSegmentShapeInitializePolicy
{
public:
	inline void initial_body() {
		(static_cast<T*>(this))->m_pBoby = cpBodyNew(INFINITY, INFINITY);
	}
	inline void initial_shape(cpSpace *space, float left, float bottom, float right, float top) {
		T* pT = static_cast<T*>(this);
		pT->m_pShape = cpSegmentShapeNew(pT->m_pBoby, cpv(left, bottom), cpv(right,top), kSegmentShapeThickness);
		pT->m_pShape->e = 0.0; pT->m_pShape->u = 1.0;
		cpSpaceAddStaticShape(space, pT->m_pShape);
	}
	
	inline void Initialize(cpSpace *space, float left, float bottom, float right, float top) {
		initial_body();
		initial_shape(space, left, bottom, right, top);
	}
	
	inline void Remove(cpSpace *space) {
		T* pT = static_cast<T*>(this);
		cpSpaceRemoveStaticShape(space, pT->m_pShape);
	}
	~StaticSegmentShapeInitializePolicy() {
		T* pT = static_cast<T*>(this);
		if(pT->m_pBoby) free(pT->m_pBoby);
	}
};

template <typename T>
struct PolygonSetVerts {
	inline void InitialCoords(const int numVerts, cpVect *verts) {
		T* pT = static_cast<T*>(this);
		pT->SetVertNum(numVerts);
		point2d_t *texCoord = pT->GetTexcoord();
		for (int i = 0; i < numVerts; i++) {
			texCoord[i] = (float_t)verts[i].x, (float_t)verts[i].y;
		}
	}
	inline void SetVerts() {
		T* pT = static_cast<T*>(this);
		cpBody *body = pT->m_pBoby;
		cpPolyShape *poly = (cpPolyShape *)pT->m_pShape;
		cpVect *verts = poly->verts;

		point2d_t *vts = pT->GetVerts();		
		for(int i=0; i<poly->numVerts; i++){
			cpVect v = cpvadd(body->p, cpvrotate(verts[i], body->rot));
			vts[i] = (float_t)v.x, (float_t)v.y;
		}
	}
};

template <typename T>
class StaticPolygonShapeInitializePolicy : public PolygonSetVerts<T>
{
public:
	inline void Initialize(cpSpace *space, const int numVerts, cpVect *verts,
						   cpVect offset, cpVect pos) {		
		T* pT = static_cast<T*>(this);
		pT->m_pBoby = cpBodyNew(INFINITY, INFINITY);
		pT->m_pShape = cpPolyShapeNew(pT->m_pBoby, numVerts, verts, cpv(0,0));
		pT->m_pShape->e = 0.0; pT->m_pShape->u = 1.0;
		cpSpaceAddShape(space, pT->m_pShape);
		pT->m_pBoby->p = pos;
	}
	
	inline void Remove(cpSpace *space) {
		T* pT = static_cast<T*>(this);
		cpSpaceRemoveStaticShape(space, pT->m_pShape);
	}
	~StaticPolygonShapeInitializePolicy() {
		T* pT = static_cast<T*>(this);
		if(pT->m_pBoby) free(pT->m_pBoby);
	}
};

template <typename T>
class PolygonShapeInitializePolicy : public PolygonSetVerts<T>
{
public:
	inline void Initialize(cpSpace *space, cpFloat mass, const int numVerts, cpVect *verts,
		cpVect offset, cpVect pos) {

		T* pT = static_cast<T*>(this);
		pT->m_pBoby = cpBodyNew(mass, cpMomentForPoly(mass, numVerts, verts, offset));
		pT->m_pBoby->p = pos;
		cpSpaceAddBody(space, pT->m_pBoby);
		pT->m_pShape = cpPolyShapeNew(pT->m_pBoby, numVerts, verts, cpv(0,0));
		pT->m_pShape->e = 0.0; pT->m_pShape->u = 1.0;
		cpSpaceAddShape(space, pT->m_pShape);
	}
	
	inline void Remove(cpSpace *space) {
		T* pT = static_cast<T*>(this);
		cpSpaceRemoveShape(space, pT->m_pShape);
		cpSpaceRemoveBody(space, pT->m_pBoby);
		cpShapeFree(pT->m_pShape);
		free(pT->m_pBoby);
	}
};

template <typename T> struct PolygonIntersectPolicy {
	inline bool intersect(cpSpace *space, const point2d_t a, const point2d_t b, 
		obj_delegate_t& d0, obj_delegate_t &d1) {

		T* pT = static_cast<T*>(this);
		cpBody *body = pT->m_pBoby;
		cpPolyShape *poly = (cpPolyShape *)pT->m_pShape;
		pT->SetVerts();
		point2d_t *vts = pT->GetVerts();
		int intersect_pos[2];
		float_t intersect_ratio[2];
		bool ret = polygon::intersect(vts, poly->numVerts, a, b, intersect_pos, intersect_ratio);
		if (false == ret) return ret;

		pPoint2d polygon0_vts, polygon1_vts;
		int num0, num1;
		polygon::CreateNewPolygonsFromIntersect(vts, poly->numVerts, intersect_pos, intersect_ratio, polygon0_vts, num0, polygon1_vts, num1);

		float_t total_area_weight;
		polygon::GetPolygonCOG(vts, poly->numVerts, total_area_weight);
		float_t weight0, weight1;
		point2d_t gravity_center0, gravity_center1;
		gravity_center0 = polygon::GetPolygonCOG(polygon0_vts, num0, weight0);
		gravity_center1 = polygon::GetPolygonCOG(polygon1_vts, num1, weight1);
		cpFloat mass0 = body->m * (weight0/total_area_weight);
		cpFloat mass1 = body->m - mass0;

		ASSERT(mass0 > 0 && mass1 > 0);
		T *polygon0 = GetNewClipPolygon(space, polygon0_vts, num0, mass0, gravity_center0, pT);
		T *polygon1 = GetNewClipPolygon(space, polygon1_vts, num1, mass1, gravity_center1, pT);
		JointsManager::updateJoints(body, polygon0->m_pBoby, polygon0_vts, num0, gravity_center0,
			polygon1->m_pBoby, gravity_center1);

		pPoint2d polygon0_coords, polygon1_coords;
		polygon::CreateNewPolygonsFromIntersect( pT->GetTexcoord(), poly->numVerts, 
			intersect_pos, intersect_ratio, polygon0_coords, num0, polygon1_coords, num1);
		polygon0->SetVertNum(num0, polygon0_vts, polygon0_coords);
		polygon1->SetVertNum(num1, polygon1_vts, polygon1_coords);
		polygon0->getTexture() = pT->getTexture();
		polygon1->getTexture() = pT->getTexture();
		polygon0->SetActions();
		polygon1->SetActions();
		ObjectManager::GetObjDelegate(polygon0, d0);
		ObjectManager::GetObjDelegate(polygon1, d1);
		return true;
	}
private:
	T *GetNewClipPolygon(cpSpace *space, const point2d_t *vts, int num, cpFloat mass, 
		point2d_t gravity_center, T *Original) {
		T *ret = ObjectManager::create_without_register<T>(); ASSERT(ret);
		boost::scoped_array<cpVect> verts(new cpVect[num]);
		for (int i = 0; i < num; i++) {
			verts[i].x = vts[i][0] - gravity_center[0];
			verts[i].y = vts[i][1] - gravity_center[1];
		}
		ret->m_pBoby = cpBodyNew(mass, cpMomentForPoly(mass, num, verts.get(), cpv(0, 0)));
		ret->m_pBoby->p.x = gravity_center[0]; ret->m_pBoby->p.y = gravity_center[1];
		ret->m_pBoby->v = Original->m_pBoby->v;
		cpSpaceAddBody(space, ret->m_pBoby);
		ret->m_pShape = cpPolyShapeNew(ret->m_pBoby, num, verts.get(), cpv(0,0));
		ret->m_pShape->e = Original->m_pShape->e; ret->m_pShape->u = Original->m_pShape->u;
		cpSpaceAddShape(space, ret->m_pShape);
		return ret;
	}
};

template <typename T>
struct CircleSetVerts{
	inline void InitialCoords() {
		T* pT = static_cast<T*>(this);
		pT->SetVertNum(4);
		point2d_t *texCoord = pT->GetTexcoord();
		texCoord[0] = 0, 1.0f;
		texCoord[1] = 0, 0.0f;
		texCoord[2] = 1.0, 0.0f;
		texCoord[3] = 1.0, 1.0f;
	}

	inline void SetVerts() {
		T* pT = static_cast<T*>(this);
		cpBody *body = pT->m_pBoby;

		cpCircleShape *circle = (cpCircleShape *)pT->m_pShape;
		cpVect center = cpvadd(body->p, cpvrotate(circle->c, body->rot));
		cpVect verts[] = {{-circle->r, -circle->r}, {-circle->r, circle->r}, 
		{circle->r, circle->r}, {circle->r, -circle->r}};

		point2d_t *vts = pT->GetVerts();		
		for(int i=0; i<4; i++) {
			verts[i] = cpvrotate(verts[i], body->rot);
			vts[i] = (float_t)(verts[i].x + center.x), (float_t)(verts[i].y + center.y);
		}
	}
};

template <typename T>
class StaticCircleShapeInitializePolicy : public CircleSetVerts<T>
{
public:
	inline void Initialize(cpSpace *space,cpFloat radius, cpVect offset, cpVect pos) {
			T* pT = static_cast<T*>(this);
			pT->m_pBoby = cpBodyNew(INFINITY, INFINITY);
			pT->m_pBoby->p = pos;
			pT->m_pShape = cpCircleShapeNew(pT->m_pBoby, radius, cpv(0,0));
			pT->m_pShape->e = 1.0; pT->m_pShape->u = 1.0;
			cpSpaceAddShape(space, pT->m_pShape);
	}

	inline void Remove(cpSpace *space) {
		T* pT = static_cast<T*>(this);
		cpSpaceRemoveStaticShape(space, pT->m_pShape);
	}
	~StaticCircleShapeInitializePolicy() {
		T* pT = static_cast<T*>(this);
		if(pT->m_pBoby) free(pT->m_pBoby);
	}
};

template <typename T>
class CircleShapeInitializePolicy : public CircleSetVerts<T>
{
public:
	inline void Initialize(cpSpace *space, cpFloat mass, cpFloat radius, cpVect offset, cpVect pos) {
			T* pT = static_cast<T*>(this);
			pT->m_pBoby = cpBodyNew(mass, cpMomentForCircle(mass,  0.0, radius, offset));
			pT->m_pBoby->p = pos;
			cpSpaceAddBody(space, pT->m_pBoby);
			pT->m_pShape = cpCircleShapeNew(pT->m_pBoby, radius, cpv(0,0));
			pT->m_pShape->e = 0.0; pT->m_pShape->u = 1.0;
			cpSpaceAddShape(space, pT->m_pShape);
	}	
	inline void Remove(cpSpace *space) {
		T* pT = static_cast<T*>(this);
		cpSpaceRemoveShape(space, pT->m_pShape);
		cpSpaceRemoveBody(space, pT->m_pBoby);
		free(pT->m_pShape);
		free(pT->m_pBoby);
	}
};

void drawObject(void *ptr, void *unused);	
template <typename T>
class DrawLineRenderPolicy
{
public:
	inline void render() {
		T* pT = static_cast<T*>(this);
		drawObject(pT->m_pShape, NULL);
	}
};

template <typename T>
class TextureRenderPolicy
{
public:
	inline point2d_t *GetTexcoord(){return m_TexcoordData.get();}
	inline point2d_t *GetVerts(){return m_VertexData.get();}

	inline void SetVertNum(const int numVerts) {
		m_ptNum = numVerts;
		m_VertexData.reset(new point2d_t[numVerts]);
		m_TexcoordData.reset(new point2d_t[numVerts]);
	}
	inline void SetVertNum(const int numVerts, point2d_t *verts, point2d_t *coors) {
		m_ptNum = numVerts;
		m_VertexData.reset(verts);
		m_TexcoordData.reset(coors);
	}
	inline void SetVertNum(const int numVerts, boost::shared_array<point2d_t> verts, boost::shared_array<point2d_t> coors){
		m_ptNum = numVerts;
		m_VertexData = verts;
		m_TexcoordData = coors;
	}
	
	inline void SetDefaultTextCoords(bool isLeftBottomFirst = true) {
		if (!m_txtPtr.get()) m_txtPtr.reset(new texture_t(m_textureName.c_str()) );
		if (NULL == m_TexcoordData.get()) SetVertNum(4);
		ASSERT( 4 == m_ptNum);
#ifndef WIN32
		Texture2D * tex = m_txtPtr->getTexture();
		if (isLeftBottomFirst) {
			m_TexcoordData[0] = 0, tex->getMaxT();
			m_TexcoordData[1] = 0.0, 0.0;
			m_TexcoordData[2] = tex->getMaxS(), 0;
			m_TexcoordData[3] = tex->getMaxS(), tex->getMaxT();
		}
		else {
			m_TexcoordData[1] = 0, tex->getMaxT();
			m_TexcoordData[2] = 0.0, 0.0;
			m_TexcoordData[3] = tex->getMaxS(), 0;
			m_TexcoordData[0] = tex->getMaxS(), tex->getMaxT();
		}

#else
		m_TexcoordData[0] = 0, ((float_t)m_txtPtr->actual_height)/m_txtPtr->height;
		m_TexcoordData[1] = 0.0, 0.0;
		m_TexcoordData[2] = ((float_t)m_txtPtr->actual_width)/m_txtPtr->width, 0;
		m_TexcoordData[3] = ((float_t)m_txtPtr->actual_width)/m_txtPtr->width, ((float_t)m_txtPtr->actual_height)/m_txtPtr->height;	
#endif		
	}

	inline void render() {

		if (!m_txtPtr.get()) m_txtPtr.reset(new texture_t(m_textureName.c_str()) );		
		//glColor4f(1.0, 1.0, 1.0, 1.0);
		glEnable(GL_TEXTURE_2D);
//#ifndef WIN32
//		glEnable(GL_MULTISAMPLE);
//#endif
//		glEnable(GL_LINE_SMOOTH);
//		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#ifdef WIN32
		glBindTexture(GL_TEXTURE_2D, m_txtPtr->texID);
#else
		glBindTexture(GL_TEXTURE_2D, m_txtPtr->getName());
#endif

		(static_cast<T*>(this))->SetVerts();
		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glVertexPointer(2, GL_FLOAT, 0, m_VertexData.get());
		glTexCoordPointer(2, GL_FLOAT, 0, m_TexcoordData.get());		
		glDrawArrays(GL_TRIANGLE_FAN, 0, m_ptNum);
		glDisable(GL_TEXTURE_2D);
		//glColor4f(0.0, 0.0, 0.0, 1.0);
	}
	inline void setTexture(const char *name) {
		m_textureName = name;
#ifndef WIN32
		if (!m_txtPtr.get()) m_txtPtr.reset(new texture_t(m_textureName.c_str()) );
#endif
	}
#ifndef WIN32
	inline void setTexture(Texture2D *tex) {
		ASSERT(!m_txtPtr.get());
		if (!m_txtPtr.get()) m_txtPtr.reset(new texture_t(tex) );
	}
#endif
	inline boost::shared_ptr<texture_t>& getTexture() {return m_txtPtr;} 
protected:
	boost::shared_ptr<texture_t> m_txtPtr;
	boost::shared_array<point2d_t> m_VertexData;
	boost::shared_array<point2d_t> m_TexcoordData;
	int m_ptNum;
	std::string m_textureName;
};

template <typename T>
class TextureWithLinesRenderPolicy : public TextureRenderPolicy<T>{
public:
	void render() {
		TextureRenderPolicy<T>::render();
		boost::scoped_array<point2d_t> lines(new point2d_t[TextureRenderPolicy<T>::m_ptNum+1]);
		memcpy(lines.get(), TextureRenderPolicy<T>::m_VertexData.get(), sizeof(point2d_t)*TextureRenderPolicy<T>::m_ptNum);
		lines[TextureRenderPolicy<T>::m_ptNum] = TextureRenderPolicy<T>::m_VertexData[0];
		glColor4f( 0.8, 0.6, 0.15, 0.5);
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(2, GL_FLOAT, 0, lines.get());
		glDrawArrays(GL_LINE_LOOP, 0, TextureRenderPolicy<T>::m_ptNum+1);
		//glColor4f(0.0, 0.0, 0.0, 1.0);
	}
};

template <typename T>
class TextureWithAnimationRenderPolicy : public TextureRenderPolicy<T>{
	
public:
	TextureWithAnimationRenderPolicy() : is_meet(false), is_happy(false){}
	
	void setBoyMeetGirl() {
		is_meet = true;
	}
	void setBoyAreHappy() {
		is_happy = true;
	}
	
	void render() {
		typedef typename TextureRenderPolicy<T>::texture_t texture_t;
		const char * tex_name[] = {
			"boy.png",
			"boy1.png",
			"boy2.png",
			"boy3.png"
		};
		
		const char *str_meet = "boy_meet.png";
		const char *str_happy[] = {
			"boy_happy_1.png",
			"boy_happy_2.png"
		};
		
		static boost::shared_ptr<texture_t> tex_meet(new texture_t(str_meet));
		static boost::shared_ptr<texture_t> tex_happy[dim(str_happy)];		
		static boost::shared_ptr<texture_t> Animations[dim(tex_name)];
		
		if (NULL == Animations[0].get()) {
			for(int i = 0; i < dim(tex_name); i++) Animations[i].reset(new texture_t(tex_name[i]));
		}
		if (NULL == tex_happy[0].get()) {
			for(int i = 0; i < dim(str_happy); i++) tex_happy[i].reset(new texture_t(str_happy[i]));
		}
		
		static int count = 0;
		
		if (is_happy) {
			++count;
			TextureRenderPolicy<T>::m_txtPtr = tex_happy[(count/kHAPPY_ANIMATION_INTERVAL)%dim(str_happy)];
		}
		else if(is_meet) {
			TextureRenderPolicy<T>::m_txtPtr = tex_meet;			
		}
		else {
			++count;
			TextureRenderPolicy<T>::m_txtPtr = Animations[(count/kANIMATION_INTERVAL)%dim(tex_name)];
		}
		TextureRenderPolicy<T>::render();
	}
private:
	bool is_meet;
	bool is_happy;
};

namespace JointsManager {
	typedef boost::function<void()> render_fn_t;
	typedef boost::function<void()> free_fn_t;
	typedef boost::function<bool(const point2d_t a, const point2d_t b)> intersect_fn_t;

	typedef struct {
		render_fn_t _render;
		free_fn_t _free;
		intersect_fn_t _intersect;
		boost::function<void(cpBody *original, 
			cpBody *clip_part0, const point2d_t *vts0, int num0, point2d_t gravity_center0, 
			cpBody *clip_part1, /*const point2d_t *vts1, int num1,*/ point2d_t gravity_center1)> _updateJoint;
		cpJoint *_joint;
	}delegator_t;
	extern std::list<delegator_t> g_jointsRenderDelegators;
	void draw_line(cpVect a, cpVect b);

	template<typename JOINT_TYPE>
	void render_joint(cpJoint *joint)
	{
		cpBody *a = joint->a;
		cpBody *b = joint->b;
		JOINT_TYPE *jnt = (JOINT_TYPE *)joint;

		jnt->r1 = cpvrotate(jnt->anchr1, a->rot);
		jnt->r2 = cpvrotate(jnt->anchr2, b->rot);
		draw_line(cpvadd(b->p, jnt->r2), cpvadd(a->p, jnt->r1));
	}

	inline void free_Joint(cpSpace *space, cpJoint *joint)
	{
		cpSpaceRemoveConstraint(space, joint);
		free(joint);
	}

	template<typename JOINT_TYPE>
	bool intersect_joint(cpJoint *joint, const point2d_t a, const point2d_t b)
	{
		cpBody *b0 = joint->a;
		cpBody *b1 = joint->b;
		JOINT_TYPE *jnt = (JOINT_TYPE *)joint;

		jnt->r1 = cpvrotate(jnt->anchr1, b0->rot);
		jnt->r2 = cpvrotate(jnt->anchr2, b1->rot);
		point2d_t c, d;
		c = jnt->r1.x + b0->p.x, jnt->r1.y + b0->p.y;
		d = jnt->r2.x + b1->p.x, jnt->r2.y + b1->p.y;
		point2d_t tmp;
		return polygon::line_intersect(a, b, c, d, tmp);
	}

	template<typename JOINT_TYPE>
	void update_joints(cpJoint *joint, cpBody *original, 
		cpBody *clip_part0, const point2d_t *vts0, int num0, point2d_t gravity_center0, 
		cpBody *clip_part1, /*const point2d_t *vts1, int num1,*/ point2d_t gravity_center1)
	{
		cpBody *b0 = joint->a;
		cpBody *b1 = joint->b;
		JOINT_TYPE *jnt = (JOINT_TYPE *)joint;

		if (b0 != original && b1!= original) return;

		point2d_t joint_pt;
		cpBody *new_body;

		jnt->r1 = cpvrotate(jnt->anchr1, b0->rot);
		jnt->r2 = cpvrotate(jnt->anchr2, b1->rot);
		if (b0 == original) 
			joint_pt = jnt->r1.x + b0->p.x, jnt->r1.y + b0->p.y;
		else 
			joint_pt = jnt->r2.x + b1->p.x, jnt->r2.y + b1->p.y;

		if (polygon::point_in_polygon(joint_pt[0], joint_pt[1], vts0, num0)) {
			new_body = clip_part0;
			joint_pt -= gravity_center0;
		}
		else {
			//ASSERT(polygon::point_in_polygon(joint_pt[0], joint_pt[1], vts1, num1));
			new_body = clip_part1;
			joint_pt -= gravity_center1;
		}
		if (b0 == original) {
			joint->a = new_body;
			jnt->anchr1 = cpv(joint_pt[0], joint_pt[1]);
		}
		else {
			joint->b = new_body;
			jnt->anchr2 = cpv(joint_pt[0], joint_pt[1]);
		}
	}

	template <typename JOINT_TYPE>
	cpJoint *create(cpSpace *space, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2, cpFloat min, cpFloat max)
	{
		cpJoint *ret = NULL;
		if (boost::is_same<cpSlideJoint, JOINT_TYPE>::value)		 
			ret = cpSlideJointNew(a, b, anchr1, anchr2, min, max);
		else if(boost::is_same<cpPinJoint, JOINT_TYPE>::value)
			ret = cpPinJointNew(a, b, anchr1, anchr2);

		ASSERT(ret);
		if (ret) {
			cpSpaceAddConstraint(space, ret);
			delegator_t tmp;
			tmp._render = boost::bind(render_joint<JOINT_TYPE>, ret);
			tmp._free = boost::bind(free_Joint, space, ret);
			tmp._intersect = boost::bind(intersect_joint<JOINT_TYPE>, ret, _1, _2);
			tmp._updateJoint = boost::bind(update_joints<JOINT_TYPE>, ret, _1, _2, _3, _4, _5, _6, _7/*, _8, _9*/);
			tmp._joint = ret;
			g_jointsRenderDelegators.push_back(tmp);
		}
		return ret;
	}

	void clearJoints();
	void RenderJoints();
	bool Intersect(const point2d_t a, const point2d_t b);
	void updateJoints(cpBody *original, 
		cpBody *clip_part0, const point2d_t *vts0, int num0, point2d_t gravity_center0, 
		cpBody *clip_part1, /*const point2d_t *vts1, int num1,*/ point2d_t gravity_center1);

};

class SpaceManger {
public:
	SpaceManger() : m_space(NULL) {initialize();}
	~SpaceManger() {release();}
	void initialize();
	void release();
	void display();
	void update(cpFloat dt);
	bool Intersect(point2d_t p0, point2d_t p1) {
		bool ret = JointsManager::Intersect(p0, p1);
		ret |= m_ObjManager->Intersect(p0, p1);
		return ret;
	}

public:
	cpSpace *m_space;
	boost::scoped_ptr<ObjectManager> m_ObjManager;
};

typedef CPhysicsObject<StaticSegmentShapeInitializePolicy> StaticEdgeObj;
typedef CPhysicsObject<StaticPolygonShapeInitializePolicy, TextureRenderPolicy> StaticPolygonObject;

typedef CPhysicsObject<PolygonShapeInitializePolicy, TextureRenderPolicy> PolygonUnclipableObject;
typedef CPhysicsObject<PolygonShapeInitializePolicy, TextureWithLinesRenderPolicy, PolygonIntersectPolicy> PolygonClipableObject;
//typedef CPhysicsObject<PolygonShapeInitializePolicy, TextureRenderPolicy, PolygonIntersectPolicy> PolygonClipableObject;
typedef CPhysicsObject<CircleShapeInitializePolicy, TextureRenderPolicy> CircleObject;
typedef CPhysicsObject<CircleShapeInitializePolicy, TextureWithAnimationRenderPolicy> CircleAnimationObject;

typedef CPhysicsObject<StaticCircleShapeInitializePolicy, TextureRenderPolicy> StaticCircleObject;


#endif //_chipmnunk_object_h_